package com.evenmo.hrms.config.jacksonConfig;

import com.evenmo.hrms.config.constantAndEnum.ConvertTypeEnum;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;


/**
 * @Description 全局配置时间类序列化和反序列化，除LocalTime返回HH:mm:ss格式字符串外其余的Date,LocalDateTime,LocalDate返回时间戳
 * @Author moyongjun
 * @Date 2022-03-25 22:51:03
 * @Version 1.0
 **/
@Configuration
public class AnyTimeToTimestampJacksonConfig {


    // 用枚举的方式结合switch控制转换配置，推荐此方式
//    ConvertTypeEnum convertTypeEnum = ConvertTypeEnum.STRING_FORMAT;

    // 从配置文件中读取配置
    // 枚举配置的字段值，对应属性文件中serializerConvertForJackson的值STRING_FORMAT
    @Value("${serializerConvertForJackson}")
    ConvertTypeEnum convertTypeEnum;


    /**
     * @return
     * @Author moyongjun
     * @Description 定义序列化和反序列化策略
     * @Date 2022-03-25 22:54:50
     * @Param
     **/
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> {
            // LocalDateTime-->timpstamp
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer());
            // LocalDate-->timpstamp
            builder.serializerByType(LocalDate.class, new LocalDateSerializer());
            /**
             * LocalTime--> HH:mm:ss
             * LocalTime 全局序列化成字符串时间，格式为 HH:mm:ss，LocalTime是不能序列化时间戳处理的，因为无日期
             *
             */
            //方式一和方式二任选一种，
            // 方式一：自定义LocalTimeSerializer,自由配置
            builder.serializerByType(LocalTime.class, new LocalTimeSerializer());
            // 方式二，不灵活，只能序列化成字符串格式
//            JavaTimeModule javaTimeModule = new JavaTimeModule();
//            javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
//            builder.modules(javaTimeModule);

            // Date-->timestamp
            builder.serializerByType(Date.class, new DateSerializer());


            // 各自的反序列化自定义类
            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer());
            builder.deserializerByType(LocalDate.class, new LocalDateDeserializer());
            builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer());
            builder.deserializerByType(Date.class, new DateDeserializer());

        };
    }

    /**
     * @Description Date类反序列化
     * @Author moyongjun
     * @Date 2022-03-26 10:38:27
     * @Version 1.0
     **/
    class DateDeserializer extends JsonDeserializer<Date> {

        @Override
        public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
            long valueAsLong = jsonParser.getValueAsLong();
            String valueAsString = jsonParser.getValueAsString();
            Date date=null;
            if (valueAsLong > 0) {
//                System.out.println("从json中读取读取到了long类型值");
               date = new Date(valueAsLong);
                return date;
            }
            if (valueAsString != null) {
//                System.out.println("从json中读取到了String类型值");
                if (valueAsString.contains("-") || valueAsString.contains(" ") || valueAsString.contains(":")) {
                    System.out.println("转换字符串格式到日期类：");
                    Instant instant = LocalDateTime.parse(valueAsString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).atZone(ZoneId.of("+8")).toInstant();
                    date = Date.from(instant);
                    return date;
                } else {
//                    System.out.println("转换字符串时间戳到日期类：");
                  date = new Date(Long.parseLong(valueAsString));
                    return date;
                }

            } else {
                return null;
            }

        }
    }

    /**
     * @Description Date序列化内部类
     * @Author moyongjun
     * @Date 2022-03-26 10:37:15
     * @Version 1.0
     **/
    class DateSerializer extends JsonSerializer<Date> {

        @Override
        public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (date != null) {


                // 枚举方式
                switch (convertTypeEnum) {
                    case TIMESTAMP:
                        long timestmp = date.toInstant().toEpochMilli();
                        jsonGenerator.writeNumber(timestmp);
                        break;
                    case STRING_FORMAT:
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        String dateformat = simpleDateFormat.format(date);
                        jsonGenerator.writeString(dateformat);
                        break;
                }


            } else {
                return;
            }
        }
    }

    /**
     * @Description LocalTime序列化输出为字符串时间
     * @Author moyongjun
     * @Date 2022-03-26 01:14:23
     * @Version 1.0
     **/
    //方式一关联的自定义内部类
    class LocalTimeSerializer extends JsonSerializer<LocalTime> {

        @Override
        public void serialize(LocalTime localTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (localTime != null) {


                // 枚举方式
                switch (convertTypeEnum) {
                    // case穿透，Time需要特殊独立序列化为字符串格式
                    case TIMESTAMP:
                    case STRING_FORMAT:
                        String stringformat = localTime.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
                        jsonGenerator.writeString(stringformat);
                        break;
                }

            } else {
                return;
            }


        }
    }

    /**
     * @Description 自定义LocalDateTime反序列化内部类
     * @Author moyongjun
     * @Date 2022-03-26 12:20:04
     * @Version 1.0
     **/
    class LocalTimeDeserializer extends JsonDeserializer<LocalTime> {

        @Override
        public LocalTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
            long timestamp = jsonParser.getValueAsLong();
            String valueAsString = jsonParser.getValueAsString();
            LocalTime localTime = null;
            if (timestamp > 0) {
               localTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("+8")).toLocalTime();
                return localTime;
            }
            if (valueAsString != null) {


                if (valueAsString.contains("-") || valueAsString.contains(":") || valueAsString.contains(" ")) {
                    localTime = LocalTime.parse(valueAsString, DateTimeFormatter.ofPattern("HH:mm:ss"));
                    return localTime;
                } else {
                    localTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(valueAsString)), ZoneId.of("+8")).toLocalTime();
                    return localTime;
                }


            } else {
                return null;
            }
        }
    }


    /**
     * @Description 自定义内部类LocalDate序列化，将LocalDate序列化为毫秒级时间戳,返回给前端
     * @Author moyongjun
     * @Date 2022-03-25 23:56:19
     * @Version 1.0
     **/
    class LocalDateSerializer extends JsonSerializer<LocalDate> {

        @Override
        public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (localDate != null) {


                // 枚举方式
                switch (convertTypeEnum) {
                    case TIMESTAMP:
                        long timestamp = localDate.atStartOfDay().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                        // 将转换后的时间戳写出去
                        jsonGenerator.writeNumber(timestamp);
                        break;
                    case STRING_FORMAT:
                        String stringformat = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        jsonGenerator.writeString(stringformat);
                        break;
                }


            } else {
                return;
            }
        }
    }

    /**
     * @Description 自定义内部类LocalDate反序列化
     * @Author moyongjun
     * @Date 2022-03-26 00:33:31
     * @Version 1.0
     **/
    class LocalDateDeserializer extends JsonDeserializer<LocalDate> {


        @Override
        public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {

            long timestamp = jsonParser.getValueAsLong();
            String valueAsString = jsonParser.getValueAsString();
            LocalDate localDate = null;
            if (timestamp > 0) {
               localDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("+8")).toLocalDate();
                return localDate;
            }
            if (valueAsString != null) {

                if (valueAsString.contains("-") || valueAsString.contains(":") || valueAsString.contains(" ")) {
                    localDate = LocalDate.parse(valueAsString, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                    return localDate;
                } else {
                    localDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(valueAsString)), ZoneId.of("+8")).toLocalDate();
                    return localDate;
                }


            } else {
                return null;
            }

        }
    }


    /**
     * @Description 自定义内部类LocalDateTime序列化，将LocalDateTime序列化为毫秒级时间戳,返回给前端
     * @Author moyongjun
     * @Date 2022-03-25 22:57:58
     * @Version 1.0
     **/
    class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

        @Override
        public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            // 将LocalDateTime转化为时间戳或字符串格式时间
            if (localDateTime != null) {

                switch (convertTypeEnum) {
                    case TIMESTAMP:
                        long timestamp = localDateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli();

                        // 将转换后的时间戳写出去
                        jsonGenerator.writeNumber(timestamp);
                        break;
                    case STRING_FORMAT:
                        String stringformat = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                        jsonGenerator.writeString(stringformat);
                }

            } else {
                return;
            }

        }


    }

    /**
     * @Description 自定义内部类LocalDateTime反序列化，将毫秒级时间戳序列化为LocalDateTime
     * @Author moyongjun
     * @Date 2022-03-25 23:00:24
     * @Version 1.0
     **/
    class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {


        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
            //获取json中得到对应转换目标的long类型值
            long timestamp = jsonParser.getValueAsLong();
            String valueAsString = jsonParser.getValueAsString();
//            System.out.println(timestamp);
            LocalDateTime localDateTime = null;
            if (timestamp > 0) {
                localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneId.of("+8"));
                return localDateTime;
            }
            if (valueAsString != null) {

                if (valueAsString.contains("-") || valueAsString.contains(":") || valueAsString.contains(" ")) {
                    localDateTime = LocalDateTime.parse(valueAsString, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    return localDateTime;
                }else {

                  localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(valueAsString)), ZoneId.of("+8"));
                    return localDateTime;
                }

            } else {
                return null;
            }

        }
    }


}
